/* 
 * Copyright [2018] [Alex/libo(liboms@hotmail.com)]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.codegen.jfinal.model;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;
import com.hyts.codegen.common.model.*;
import com.hyts.guava.String.Strings;
import com.hyts.guava.object.Objects;
import com.hyts.template.config.JavaTemplateConfig.JavaType;

/** 
 * @title JfinalBuildModel.java  
 * @package com.hyts.codegen.jfinal.model  
 * @description Jfinal特定封装模型类：用于覆盖模型所需数据  
 * @see JavaModel
 * @author libo/Alex
 * @email liboms@hotmail.com/liboware@163.com  
 * @date 2018年5月12日 
 * @version V1.0 
 */
public class JfinalBuildModel extends JavaModel{

	
	private SimpleBuildModel model;
	
	/**  
	 * @constructor：JfinalBuildModel  
	 * @param model
	 */ 
	public JfinalBuildModel(SimpleBuildModel model) {
		Objects.checkNull(model);
		this.setBasePackageName(Strings.compareBlankAndSet(model.getBasePackageName(),"."));
		if(Objects.isNull(model.getJavaDocMap())){
			model.setJavaDocMap(new HashMap<>());
			model.getJavaDocMap().put("author","alex.libo");
			model.getJavaDocMap().put("description", "generate by coffice-toolbox-codegen");
			model.getJavaDocMap().put("date", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
		}
		this.model = model;
	}

	/**  
	 * <b>initModel:初始化模型类</b>
	 * <p>初始化模型类:准备可以指定的数据信息</p>
	 * @return
	 * @exception
	 */ 
	public JfinalBuildModel initModel(){
		//javaDoc模型构建
		JavaDocModel model = new JavaDocModel(this.model.getJavaDocMap(),"");
		this.setDocModel(model);
		this.setModelPackageName(this.model.getModelPackageName());
		//引入包信息
		this.setImportClasses(this.model.getImportClassName());
		if(Objects.isNull(this.getImportClasses())){
			this.setImportClasses(new String[]{"import com.jfinal.plugin.activerecord.Model"});
		}else {
			String [] tempArray = new String[this.getImportClasses().length+1];
			tempArray[this.getImportClasses().length] = "import com.jfinal.plugin.activerecord.Model";
			System.arraycopy(this.getImportClasses(), 0,tempArray , 0, this.getImportClasses().length);
			this.setImportClasses(tempArray);
		}
		//引入类信息
		JavaTypeModel model2 = new JavaTypeModel(); 
		model2.setAccessType("public");
		model2.setAnnotations(this.model.getAnnotations());
		model2.setClassName(this.model.getJavaTypeName()+" extends Model<"+this.model.getJavaTypeName()+">");
		model2.setJavaType(JavaType.TYPE);
		this.setTypeModel(model2);
		//初始化属性模型
		List<JavaFieldModel> fields = new LinkedList<>();
		JavaFieldModel model3 = new JavaFieldModel();
		model3.setHeadSpace("    ");
		model3.setReturnType(this.model.getJavaTypeName());
		model3.setAccessType("public static final");
		model3.setFieldName(" DAO = new "+this.model.getJavaTypeName()+"()");
		fields.add(model3);
		this.setFieldModels(fields);
		return this;
	}
	
	/**  
	 * <b>initDao</b>
	 * <p>初始化数据访问层</p>
	 * @return
	 * @exception
	 */ 
	public JfinalBuildModel initDao(){
		//javaDoc模型构建
		JavaDocModel model = new JavaDocModel(this.model.getJavaDocMap(),"");
		this.setDocModel(model);
		this.setModelPackageName(this.model.getModelPackageName());
		//引入包信息
		this.setImportClasses(this.model.getImportClassName());
		//暂时保留对model类的引用
		if(Objects.isNull(this.getImportClasses())){
			this.setImportClasses(new String[]{"import com.jfinal.plugin.activerecord.Model"});
		}else {
			String [] tempArray = new String[this.getImportClasses().length+5];
			tempArray[this.getImportClasses().length] = "import com.jfinal.plugin.activerecord.Model";
			tempArray[this.getImportClasses().length+1] = "import com.hyts.jfmvc.scan.annotation.Repository";
			tempArray[this.getImportClasses().length+2] = "import com.jfinal.plugin.activerecord.Page";
			tempArray[this.getImportClasses().length+3] = "import com.jfinal.plugin.activerecord.TableMapping";
			tempArray[this.getImportClasses().length+4] = "import com.hyts.jfmvc.util.StringUtils";
			System.arraycopy(this.getImportClasses(), 0,tempArray , 0, this.getImportClasses().length);
			this.setImportClasses(tempArray);
		}
		//引入类信息
		JavaTypeModel model2 = new JavaTypeModel();
		model2.setAccessType("public");
		model2.setAnnotations(this.model.getAnnotations());
		model2.setClassName(this.model.getJavaTypeName());
		model2.setJavaType(JavaType.TYPE);
		this.setTypeModel(model2);

		//初始化方法模型
		List<JavaMethodModel> methods = new LinkedList<>();
		String beanClassName = this.getImportClasses()[0].substring(this.getImportClasses()[0].lastIndexOf(".")+1);

		//Query插入的方法
		JavaMethodModel queryMethod = new JavaMethodModel();
		queryMethod.setHeadSpace("    ");
		queryMethod.setAccessType("public");
		StringBuilder sb = new StringBuilder();
		sb.append(String.format("    Set<Map.Entry<String, Object>> params = %s._getAttrsEntrySet();\n",Strings.upperCamelToLowerCamel(beanClassName)));
		sb.append(String.format("        String tableName = TableMapping.me().getTable(%s.getClass()).getName();\n",Strings.upperCamelToLowerCamel(beanClassName)));
		sb.append("        StringBuilder sql = new StringBuilder(\"FROM \").append(tableName).append(\" WHERE 1 = 1 \");\n");
		sb.append("        List<Object> paramValues = new LinkedList<Object>();\n");
		sb.append("        for(Map.Entry<String, Object> param : params){\n");
		sb.append("            if(StringUtils.isNotEmpty(param.getKey()) && param.getValue() != null){\n");
		sb.append("                sql.append(\" AND \").append(param.getKey()).append(\" = ?\");\n");
		sb.append("                paramValues.add(param.getValue());\n");
		sb.append("           }\n");
		sb.append("       }\n");
		sb.append(String.format("       return %s.DAO.paginate(pageNum,pageSize,\"SELECT *\",sql.toString(),paramValues.toArray(new Object[paramValues.size()]));\n",beanClassName));
		queryMethod.setFunctionBody(sb.toString());
		queryMethod.setMethodName("query");
		queryMethod.setReturnType(String.format("Page<%s>",beanClassName));
		Map<String,String> paramList = Maps.newTreeMap();
		paramList.put(beanClassName,Strings.upperCamelToLowerCamel(beanClassName));
		paramList.put("byte","pageSize");
		paramList.put("int","pageNum");
		queryMethod.setParamList(paramList);
		methods.add(queryMethod);

		//Insert插入的方法
		JavaMethodModel insertMethod = new JavaMethodModel();
		insertMethod.setHeadSpace("    ");
		insertMethod.setAccessType("public");
		insertMethod.setFunctionBody("    return "+String.format(beanClassName+".DAO._setAttrs(%s).save();\n",Strings.upperCamelToLowerCamel(beanClassName)));
		insertMethod.setMethodName("insert");
		insertMethod.setReturnType("boolean");
		paramList = Maps.newHashMap();
		paramList.put(beanClassName,Strings.upperCamelToLowerCamel(beanClassName));
		insertMethod.setParamList(paramList);
		methods.add(insertMethod);

		//Delete删除的方法
		JavaMethodModel deleteMethod = new JavaMethodModel();
		deleteMethod.setHeadSpace("    ");
		deleteMethod.setAccessType("public");
		deleteMethod.setFunctionBody("    return "+beanClassName+".DAO.deleteById(idValues);\n");
		deleteMethod.setMethodName("delete");
		deleteMethod.setReturnType("boolean");
		paramList = Maps.newHashMap();
		paramList.put("Object...","idValues");
		deleteMethod.setParamList(paramList);
		methods.add(deleteMethod);

		//Update更新的方法
		JavaMethodModel updateMethod = new JavaMethodModel();
		updateMethod.setHeadSpace("    ");
		updateMethod.setAccessType("public");
		updateMethod.setFunctionBody("    return "+String.format(beanClassName+".DAO._setAttrs(%s).update();\n",Strings.upperCamelToLowerCamel(beanClassName)));
		updateMethod.setMethodName("update");
		updateMethod.setReturnType("boolean");
		paramList = Maps.newHashMap();
		paramList.put(beanClassName,Strings.upperCamelToLowerCamel(beanClassName));
		updateMethod.setParamList(paramList);
		methods.add(updateMethod);

		//Load加载信息方法
		JavaMethodModel loadMethod = new JavaMethodModel();
		loadMethod.setHeadSpace("    ");
		loadMethod.setAccessType("public");
		loadMethod.setFunctionBody("    return "+beanClassName+".DAO.findById(id);\n");
		loadMethod.setMethodName("load");
		loadMethod.setReturnType(beanClassName);
		paramList = Maps.newHashMap();
		paramList.put("Object","id");
		loadMethod.setParamList(paramList);
		methods.add(loadMethod);

		this.setMethodModels(methods);
		return this;
	}


	/**
	 * <b>initService</b>
	 * <p>初始化业务逻辑层</p>
	 * @return
	 * @exception
	 */
	public JfinalBuildModel initService(boolean isInterface) {
		//javaDoc模型构建
		JavaDocModel model = new JavaDocModel(this.model.getJavaDocMap(), "");
		this.setDocModel(model);
		this.setModelPackageName(this.model.getModelPackageName());
		//引入包信息
		this.setImportClasses(this.model.getImportClassName());
		//暂时保留对model类的引用
		String[] tempArray = null;
		if (Objects.isNull(this.getImportClasses())) {
			this.setImportClasses(new String[]{"import com.jfinal.plugin.activerecord.Model"});
		} else {
			if (isInterface) {
				tempArray = new String[this.getImportClasses().length + 1];
				tempArray[this.getImportClasses().length] = "import com.jfinal.plugin.activerecord.Page";
			} else {
				tempArray = new String[this.getImportClasses().length + 3];
				tempArray[this.getImportClasses().length] = "import com.jfinal.plugin.activerecord.Page";
				tempArray[this.getImportClasses().length + 1] = "import com.hyts.jfmvc.scan.annotation.Resource";
				tempArray[this.getImportClasses().length + 2] = "import com.hyts.jfmvc.scan.annotation.Service";
			}
			System.arraycopy(this.getImportClasses(), 0, tempArray, 0, this.getImportClasses().length);
			this.setImportClasses(tempArray);
		}
		//引入类信息
		JavaTypeModel model2 = new JavaTypeModel();
		model2.setAccessType("public");
		model2.setAnnotations(this.model.getAnnotations());
		if (isInterface){
			model2.setJavaType(JavaType.INTERFACE);
			model2.setClassName(this.model.getJavaTypeName());
		}else{
			model2.setJavaType(JavaType.TYPE);
			String interfaceName = tempArray[1].substring(tempArray[1].lastIndexOf(".")+1);//this.model.getJavaTypeName().substring(0, this.model.getJavaTypeName().lastIndexOf("Impl"));
			model2.setClassName(isInterface ? this.model.getJavaTypeName() : this.model.getJavaTypeName() + " implements " + interfaceName);
			JavaFieldModel fieldModel = new JavaFieldModel();
			fieldModel.setHeadSpace("    ");
			fieldModel.setAccessType("public");
			fieldModel.setFieldName("dao");
			fieldModel.setReturnType(tempArray[2].substring(tempArray[2].lastIndexOf(".")+1));
			fieldModel.setAnnotations(new String[]{"Resource"});
			this.setFieldModels(new ArrayList(){{
				add(fieldModel);
			}});
		}
		this.setTypeModel(model2);
		//初始化方法模型
		List<JavaMethodModel> methods = new LinkedList<>();
		String beanClassName = this.getImportClasses()[0].substring(this.getImportClasses()[0].lastIndexOf(".")+1);

		//Query插入的方法
		JavaMethodModel queryMethod = new JavaMethodModel();
		queryMethod.setHeadSpace("    ");
		String functionBody = "    return dao.query(%s,pageSize,pageNum);\n";
		queryMethod.setFunctionBody(isInterface?"":String.format(functionBody,Strings.upperCamelToLowerCamel(beanClassName)));
		queryMethod.setMethodName("query");
		queryMethod.setReturnType(String.format("Page<%s>",beanClassName));
		Map<String,String> paramList = Maps.newTreeMap();
		paramList.put("byte","pageSize");
		paramList.put("int","pageNum");
		paramList.put(beanClassName,Strings.upperCamelToLowerCamel(beanClassName));
		queryMethod.setParamList(paramList);
		methods.add(queryMethod);

		//Insert插入的方法
		JavaMethodModel insertMethod = new JavaMethodModel();
		insertMethod.setHeadSpace("    ");
		functionBody = String.format("    return dao.insert(%s);\n",Strings.upperCamelToLowerCamel(beanClassName));
		insertMethod.setFunctionBody(isInterface?"":functionBody);
		insertMethod.setMethodName("insertData");
		insertMethod.setReturnType("boolean");
		paramList = Maps.newHashMap();
		paramList.put(beanClassName,Strings.upperCamelToLowerCamel(beanClassName));
		insertMethod.setParamList(paramList);
		methods.add(insertMethod);

		//Delete删除的方法
		JavaMethodModel deleteMethod = new JavaMethodModel();
		deleteMethod.setHeadSpace("    ");
		functionBody = "    return dao.delete(idValues);\n";
		deleteMethod.setFunctionBody(isInterface?"":functionBody);
		deleteMethod.setMethodName("deleteData");
		deleteMethod.setReturnType("boolean");
		paramList = Maps.newHashMap();
		paramList.put("Object...","idValues");
		deleteMethod.setParamList(paramList);
		methods.add(deleteMethod);

		//Update更新的方法
		JavaMethodModel updateMethod = new JavaMethodModel();
		updateMethod.setHeadSpace("    ");
		functionBody = "    return dao.update(%s);\n";
		updateMethod.setFunctionBody(isInterface?"":String.format(functionBody,Strings.upperCamelToLowerCamel(beanClassName)));
		updateMethod.setMethodName("updateData");
		updateMethod.setReturnType("boolean");
		paramList = Maps.newHashMap();
		paramList.put(beanClassName,Strings.upperCamelToLowerCamel(beanClassName));
		updateMethod.setParamList(paramList);
		methods.add(updateMethod);

		//Load加载信息方法
		JavaMethodModel loadMethod = new JavaMethodModel();
		loadMethod.setHeadSpace("    ");
		functionBody = "    return dao.load(id);\n";
		loadMethod.setMethodName("loadData");
		loadMethod.setFunctionBody(isInterface?"":functionBody);
		loadMethod.setReturnType(beanClassName);
		paramList = Maps.newHashMap();
		paramList.put("Object","id");
		loadMethod.setParamList(paramList);
		methods.add(loadMethod);

		this.setMethodModels(methods);
		return this;
	}

	/**
	 * <b>initController</b>
	 * <p>初始化访问控制层</p>
	 * @return
	 * @exception
	 */
	public JfinalBuildModel initController() {
		//javaDoc模型构建
		JavaDocModel model = new JavaDocModel(this.model.getJavaDocMap(), "");
		this.setDocModel(model);
		this.setModelPackageName(this.model.getModelPackageName());
		//引入包信息
		this.setImportClasses(this.model.getImportClassName());
		//暂时保留对model类的引用
		String[] tempArray = null;
		if (Objects.isNull(this.getImportClasses())) {
			this.setImportClasses(new String[]{"import com.jfinal.plugin.activerecord.Model"});
		} else {
			tempArray = new String[this.getImportClasses().length + 5];
			tempArray[this.getImportClasses().length] = "import com.jfinal.plugin.activerecord.Page";
			tempArray[this.getImportClasses().length + 1] = "import com.hyts.jfmvc.scan.annotation.Resource";
			tempArray[this.getImportClasses().length + 2] = "import com.hyts.jfmvc.scan.annotation.Service";
			tempArray[this.getImportClasses().length + 3] = "import com.hyts.jfmvc.scan.annotation.Controller";
			tempArray[this.getImportClasses().length + 4] = "import com.hyts.jfmvc.web.controller.BaseController";
			System.arraycopy(this.getImportClasses(), 0, tempArray, 0, this.getImportClasses().length);
			this.setImportClasses(tempArray);
		}
		//引入类信息
		JavaTypeModel model2 = new JavaTypeModel();
		model2.setAccessType("public");
		model2.setAnnotations(this.model.getAnnotations());
		model2.setJavaType(JavaType.TYPE);
		model2.setClassName(this.model.getJavaTypeName()+" extends BaseController");
		JavaFieldModel fieldModel = new JavaFieldModel();
		fieldModel.setHeadSpace("    ");
		fieldModel.setAccessType("public");
		fieldModel.setFieldName("service");
		fieldModel.setReturnType(tempArray[1].substring(tempArray[1].lastIndexOf(".")+1));
		fieldModel.setAnnotations(new String[]{"Resource"});
		this.setFieldModels(new ArrayList(){{
			add(fieldModel);
		}});
		this.setTypeModel(model2);
		//初始化方法模型
		List<JavaMethodModel> methods = new LinkedList<>();
		String beanClassName = this.getImportClasses()[0].substring(this.getImportClasses()[0].lastIndexOf(".")+1);

		//Query插入的方法
		JavaMethodModel indexMethod = new JavaMethodModel();
		indexMethod.setHeadSpace("    ");
		String functionBody = "    render(\"index\");\n";
		indexMethod.setFunctionBody(functionBody);
		indexMethod.setMethodName("index");
		indexMethod.setReturnType("void");
		methods.add(indexMethod);

		//Query插入的方法
		JavaMethodModel queryMethod = new JavaMethodModel();
		queryMethod.setHeadSpace("    ");
		StringBuilder functionBodyAppend = new StringBuilder("     JSON(Operation.QUERY, service.query(this.getModel(%s.class,true),this.getPageSize(),this.getPageNum()).getList());\n");
		queryMethod.setFunctionBody(String.format(functionBodyAppend.toString(),beanClassName));
		queryMethod.setMethodName("queryForList");
		queryMethod.setReturnType("void");
		methods.add(queryMethod);

		//Insert插入的方法-01
		JavaMethodModel addForModelPage = new JavaMethodModel();
		addForModelPage.setHeadSpace("    ");
		functionBodyAppend = new StringBuilder("     render(\"addForModel\");\n");
		addForModelPage.setFunctionBody(String.format(functionBodyAppend.toString(),beanClassName));
		addForModelPage.setMethodName("addForModelPage");
		addForModelPage.setReturnType("void");
		methods.add(addForModelPage);

		//Insert插入方法-02
		JavaMethodModel addForModel = new JavaMethodModel();
		addForModel.setHeadSpace("    ");
		functionBodyAppend = new StringBuilder(String.format("     JSON(Operation.ADD,service.insertData(this.getModel(%s.class,true)));\n",beanClassName));
		addForModel.setFunctionBody(String.format(functionBodyAppend.toString(),beanClassName));
		addForModel.setMethodName("addForModel");
		addForModel.setReturnType("void");
		methods.add(addForModel);

		//Delete删除的方法
		JavaMethodModel deleteMethod = new JavaMethodModel();
		deleteMethod.setHeadSpace("    ");
		functionBody = "    JSON(Operation.DELETE,service.deleteData(this.getPara(DEFAULT_IDENTIFIED_TOKEN)));\n";
		deleteMethod.setFunctionBody(functionBody);
		deleteMethod.setMethodName("removeForModel");
		deleteMethod.setReturnType("void");
		methods.add(deleteMethod);


		//Update更新的方法-01
		JavaMethodModel editForModelPage = new JavaMethodModel();
		editForModelPage.setHeadSpace("    ");
		functionBody = "     render(\"editForModel\");\n";
		editForModelPage.setFunctionBody(functionBody);
		editForModelPage.setMethodName("editForModelPage");
		editForModelPage.setReturnType("void");
		methods.add(editForModelPage);

		//Update更新的方法-02
		JavaMethodModel updateMethod = new JavaMethodModel();
		updateMethod.setHeadSpace("    ");
		functionBody = "    JSON(Operation.EDIT,service.updateData(this.getModel(%s.class,true)));\n";
		updateMethod.setFunctionBody(String.format(functionBody,beanClassName));
		updateMethod.setMethodName("editForModel");
		updateMethod.setReturnType("void");
		methods.add(updateMethod);


		//Load加载信息方法
		JavaMethodModel loadMethod = new JavaMethodModel();
		loadMethod.setHeadSpace("    ");
		functionBody = "    JSON(Operation.LOAD,service.loadData(this.getPara(DEFAULT_IDENTIFIED_TOKEN)));\n";
		loadMethod.setMethodName("loadForModel");
		loadMethod.setFunctionBody(functionBody);
		loadMethod.setReturnType("void");
		methods.add(loadMethod);

		this.setMethodModels(methods);
		return this;
	}

	/**  
	 * @name model's getter method
	 * @return model
	 */
	public SimpleBuildModel getModel() {
		return model;
	}

	/**  
	 * @name model's setter method
	 * @param model
	 * @return void
	 */
	public void setModel(SimpleBuildModel model) {
		this.model = model;
	}
	
}
